package com.itheima.bigevent.aspect;

import com.itheima.bigevent.annotation.CategoryLog;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author wuhaohua
 */
@Aspect
@Component
public class CategoryAspect {
    /**
     * 定义了一个切点
     * 这里的路径填自定义注解的全路径
     */
    @Pointcut("@annotation(com.itheima.bigevent.annotation.CategoryLog)")
    public void categoryCut() {

    }

    @Before("categoryCut()")
    public void before(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        Map<String, Object> paramMap = new HashMap(16);
        Parameter[] parameters = method.getParameters();
        Object[] parameterValues = joinPoint.getArgs();
        if (!ObjectUtils.isEmpty(parameters)) {
            for (int i = 0; i < parameters.length; ++i) {
                Map<String, Object> subPlaceholder = this.getAttrs(parameters[i], parameterValues[i]);
                paramMap.putAll(subPlaceholder);
            }
        }

        paramMap.forEach((k, v) -> System.out.println("参数：" + k + "=" + v));
//        parameters[0]

        CategoryLog categoryLog = method.getAnnotation(CategoryLog.class);
        if (!ObjectUtils.isEmpty(categoryLog)) {
            String[] field = categoryLog.field();
            String str = (String) paramMap.get(field[0]);
            System.out.println("参数：" + str);
        }

        System.out.println("注解方式AOP开始拦截, 当前拦截的方法名: " + method.getName());
    }

    /**
     * 获取参数
     * @param parameter
     * @param value
     *
     * @return
     */
    private Map<String, Object> getAttrs(Parameter parameter, Object value) {
        Map<String, Object> map = new HashMap<>();
        if (parameter.getType().getClassLoader() == null) {
            map = this.getJdkTypeAttrs(parameter, value);
        } else {
            map = this.getCustomObjAttrs(value);
        }
        return map;

    }

    /**
     * 获取jdk自带类型的参数
     * 8种基本数据类型、List、Map
     * @param parameter
     * @param value
     *
     * @return
     */
    private Map<String, Object> getJdkTypeAttrs(Parameter parameter, Object value) {
        Map<String, Object> map = new HashMap<>();
        if (value instanceof Integer
                || value instanceof Long
                || value instanceof Short
                || value instanceof Float
                || value instanceof Byte
                || value instanceof String
                || value instanceof Boolean
                || value instanceof Double) {
            map.put(parameter.getName(), value);
            return map;
        }
        if (value instanceof List) {
            map.putAll(this.getCustomObjAttrs(((List) value).get(0)));
            return map;
        }
        if (value instanceof Map) {
            map.putAll((Map<? extends String, ?>) value);
            return map;
        }
        System.out.println("参数类型不支持 type："+ parameter.getType().getName());
        return map;
    }

    /**
     * 获取自定义对象类型的参数
     *
     * @param object
     * @return
     */
    private Map<String, Object> getCustomObjAttrs(Object object) {
        Map<String, Object> map = new HashMap(16);
        Class<?> clazz = object.getClass();
        Field[] fields = clazz.getDeclaredFields();
        if (fields != null && fields.length != 0) {
            int length = fields.length;
            for (int i = 0; i < length; ++i) {
                Field field = fields[i];
                try {
                    field.setAccessible(true);
                    Object value = field.get(object);
                    if (value != null) {
                        map.put(field.getName(), value);
                    }
                } catch (Exception e) {
                    System.out.println("读取属性失败");
                    e.printStackTrace();
                }
            }
            return map;
        } else {
            return map;
        }
    }

    @After("categoryCut()")
    public void after(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP执行的方法 :" + method.getName() + " 执行完了");
    }

    @Around("categoryCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("注解方式AOP拦截开始进入环绕通知.......");
        Object proceed = joinPoint.proceed();
        System.out.println("准备退出环绕......");
        return proceed;
    }

    /**
     * returning属性指定连接点方法返回的结果放置在result变量中
     *
     * @param joinPoint 连接点
     * @param result    返回结果
     */
    @AfterReturning(value = "categoryCut()", returning = "result")
    public void afterReturn(JoinPoint joinPoint, Object result) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP拦截的方法执行成功, 进入返回通知拦截, 方法名为: " + method.getName() + ", 返回结果为: " + result.toString());
    }

    @AfterThrowing(value = "categoryCut()", throwing = "e")
    public void afterThrow(JoinPoint joinPoint, Exception e) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        System.out.println("注解方式AOP进入方法异常拦截, 方法名为: " + method.getName() + ", 异常信息为: " + e.getMessage());
    }
}
